package com.xinggq.common.exception;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import com.xinggq.common.CommonResult;
import com.xinggq.common.context.TraceContext;
import com.xinggq.common.enums.ResponseEnum;
import feign.FeignException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import org.springframework.web.method.annotation.HandlerMethodValidationException;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 基础全局异常处理器
 * 提供通用的异常处理逻辑，统一响应格式和日志记录
 *
 * <p>设计说明：</p>
 * <ul>
 *   <li>所有异常处理方法都返回HTTP 200状态码，通过响应体中的业务状态码区分错误类型</li>
 *   <li>这样设计的好处是前端可以统一处理所有响应，不需要根据HTTP状态码做特殊处理</li>
 *   <li>业务状态码在CommonResult中的code字段中体现，便于前端统一处理</li>
 * </ul>
 *
 * @author xinggq
 * @since 2025-01-20
 */
@Slf4j
// 设置较低优先级，允许业务模块覆盖
@Order(100)
public abstract class BaseGlobalExceptionHandler {

  /**
   * 提取多余的字段名
   */
  private static final Pattern PATTERN = Pattern.compile("Unrecognized field \"(.*?)\"");

  /**
   * 处理自定义业务异常
   */
  @ExceptionHandler(AbstractCommonException.class)
  public CommonResult<Object> handleCommonException(AbstractCommonException e,
      HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();
    log.error("业务异常 [TraceId: {}] - 请求路径: {}, 错误码: {}, 错误信息: {}",
        traceId, request.getRequestURI(), e.getCode(), e.getMessage(), e);

    return CommonResult.result(e.getCode(), e.getMessage()).setData(e.getData());
  }

  /**
   * 处理参数校验异常（@Valid注解）
   */
  @ExceptionHandler(MethodArgumentNotValidException.class)
  public CommonResult<Object> handleValidationException(MethodArgumentNotValidException e,
      HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();
    log.error("参数校验异常 [TraceId: {}] - 请求路径: {}", traceId, request.getRequestURI(), e);

    Map<String, String> errors = new HashMap<>(e.getBindingResult().getFieldErrors().size());
    for (FieldError error : e.getBindingResult().getFieldErrors()) {
      errors.put(error.getField(), error.getDefaultMessage());
    }

    return CommonResult.result(ResponseEnum.PARAMS_ERROR.getCode(), "参数校验失败").setData(errors);
  }

  /**
   * 处理参数绑定异常
   */
  @ExceptionHandler(BindException.class)
  public CommonResult<Object> handleBindException(BindException e, HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();
    log.error("参数绑定异常 [TraceId: {}] - 请求路径: {}", traceId, request.getRequestURI(), e);

    Map<String, String> errors = new HashMap<>(e.getBindingResult().getFieldErrors().size());
    for (FieldError error : e.getBindingResult().getFieldErrors()) {
      errors.put(error.getField(), error.getDefaultMessage());
    }

    return CommonResult.result(ResponseEnum.PARAMS_ERROR.getCode(), "参数绑定失败").setData(errors);
  }

  /**
   * 处理约束违反异常（@Validated注解）
   */
  @ExceptionHandler(ConstraintViolationException.class)
  public CommonResult<Object> handleConstraintViolationException(ConstraintViolationException e,
      HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();
    log.error("约束违反异常 [TraceId: {}] - 请求路径: {}", traceId, request.getRequestURI(), e);

    Map<String, String> errors = new HashMap<>(e.getConstraintViolations().size());
    Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
    for (ConstraintViolation<?> violation : violations) {
      errors.put(violation.getPropertyPath().toString(), violation.getMessage());
    }

    return CommonResult.result(ResponseEnum.PARAMS_ERROR.getCode(), "约束违反").setData(errors);
  }

  /**
   * 处理方法级别参数验证异常（Spring 6.x新增）
   */
  @ExceptionHandler(HandlerMethodValidationException.class)
  public CommonResult<Object> handleHandlerMethodValidationException(HandlerMethodValidationException e,
      HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();
    log.error("方法参数验证异常 [TraceId: {}] - 请求路径: {}", traceId, request.getRequestURI(), e);

    Map<String, String> errors = new HashMap<>();
    
    // 处理参数验证错误
    e.getValueResults().forEach(parameterValidationResult -> {
      final String parameterName = parameterValidationResult.getMethodParameter().getParameterName() != null
          ? parameterValidationResult.getMethodParameter().getParameterName()
          : "parameter" + parameterValidationResult.getMethodParameter().getParameterIndex();
      
      parameterValidationResult.getResolvableErrors().forEach(error -> {
        if (error instanceof ConstraintViolation<?> violation) {
          String fieldPath = violation.getPropertyPath().toString();
          if (fieldPath.isEmpty()) {
            fieldPath = parameterName;
          } else {
            fieldPath = parameterName + "." + fieldPath;
          }
          errors.put(fieldPath, violation.getMessage());
        } else {
          errors.put(parameterName, error.getDefaultMessage());
        }
      });
    });

    String message = errors.isEmpty() ? "方法参数验证失败" : "参数验证失败: " + errors.values().iterator().next();
    return CommonResult.result(ResponseEnum.PARAMS_ERROR.getCode(), message).setData(errors);
  }

  /**
   * 处理资源未找到异常（404错误）
   */
  @ExceptionHandler(NoResourceFoundException.class)
  public CommonResult<Object> handleNoResourceFoundException(NoResourceFoundException e,
      HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();

    String resourcePath = e.getResourcePath();

    log.error("资源未找到异常 [TraceId: {}] - 请求路径: {}, 资源路径: {}",
        traceId, request.getRequestURI(), resourcePath, e);

    String message = String.format("请求的资源不存在：%s", resourcePath);
    return CommonResult.result(ResponseEnum.URL_ERROR.getCode(), message);
  }

  /**
   * 处理不支持的媒体类型异常
   */
  @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
  public CommonResult<Object> handleHttpMediaTypeNotSupportedException(
      HttpMediaTypeNotSupportedException e, HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();

    String contentType = e.getContentType() != null ? e.getContentType().toString() : "未知";
    String supportedTypes = e.getSupportedMediaTypes().toString();

    log.error(
        "不支持的媒体类型异常 [TraceId: {}] - 请求路径: {}, 请求Content-Type: {}, 支持的类型: {}",
        traceId, request.getRequestURI(), contentType, supportedTypes, e);

    String message = String.format("不支持的Content-Type: %s，请使用: %s", contentType,
        supportedTypes);
    return CommonResult.result(ResponseEnum.INVALID_PARAM.getCode(), message);
  }

  /**
   * 处理缺少必需请求参数异常
   */
  @ExceptionHandler(MissingServletRequestParameterException.class)
  public CommonResult<Object> handleMissingServletRequestParameterException(
      MissingServletRequestParameterException e, HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();

    String parameterName = e.getParameterName();
    String parameterType = e.getParameterType();

    log.error("缺少必需请求参数异常 [TraceId: {}] - 请求路径: {}, 缺少参数: {} (类型: {})",
        traceId, request.getRequestURI(), parameterName, parameterType, e);

    String message = String.format("缺少必需的请求参数：%s (类型: %s)", parameterName,
        parameterType);
    return CommonResult.result(ResponseEnum.PARAMS_ERROR.getCode(), message);
  }

  /**
   * 处理HTTP请求方法不支持异常
   */
  @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
  public CommonResult<Object> handleHttpRequestMethodNotSupportedException(
      HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();

    String method = e.getMethod();
    String[] supportedMethods = e.getSupportedMethods();
    String supportedMethodsStr = supportedMethods != null ? String.join(", ", supportedMethods) : "未知";

    log.error("HTTP请求方法不支持异常 [TraceId: {}] - 请求路径: {}, 请求方法: {}, 支持的方法: {}",
        traceId, request.getRequestURI(), method, supportedMethodsStr, e);

    String message = String.format("不支持的请求方法：%s，该接口支持的方法：%s", method, supportedMethodsStr);
    return CommonResult.result(ResponseEnum.INVALID_PARAM.getCode(), message);
  }

  /**
   * 处理参数类型转换异常
   */
  @ExceptionHandler(MethodArgumentTypeMismatchException.class)
  public CommonResult<Object> handleTypeMismatchException(MethodArgumentTypeMismatchException e,
      HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();
    String parameterName = e.getName();
    String parameterType =
        e.getRequiredType() != null ? e.getRequiredType().getSimpleName() : "Unknown";
    String inputValue = e.getValue() != null ? e.getValue().toString() : "null";

    log.error("参数类型转换异常 [TraceId: {}] - 请求路径: {}, 参数名: {}, 期望类型: {}, 输入值: {}",
        traceId, request.getRequestURI(), parameterName, parameterType, inputValue, e);

    String errorMessage = String.format("参数 '%s' 类型错误，期望类型: %s，实际输入: %s",
        parameterName, parameterType, inputValue);

    return CommonResult.result(ResponseEnum.PARAMS_ERROR.getCode(), errorMessage);
  }

  /**
   * 处理非法参数异常
   */
  @ExceptionHandler(IllegalArgumentException.class)
  public CommonResult<Object> handleIllegalArgumentException(IllegalArgumentException e,
      HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();
    log.error("非法参数异常 [TraceId: {}] - 请求路径: {}, 错误信息: {}",
        traceId, request.getRequestURI(), e.getMessage(), e);
    return CommonResult.result(ResponseEnum.PARAMS_ERROR.getCode(), e.getMessage());
  }

  /**
   * 处理空指针异常
   */
  @ExceptionHandler(NullPointerException.class)
  public CommonResult<Object> handleNullPointerException(NullPointerException e,
      HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();
    log.error("空指针异常 [TraceId: {}] - 请求路径: {}", traceId, request.getRequestURI(), e);
    return CommonResult.result(ResponseEnum.UNKNOWN_ERROR.getCode(), "系统内部错误");
  }

  /**
   * 处理HTTP消息不可读异常（包括JSON解析异常）
   */
  @ExceptionHandler(HttpMessageNotReadableException.class)
  public CommonResult<Object> handleHttpMessageNotReadableException(
      HttpMessageNotReadableException e,
      HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();

    // 检查是否是缺少请求体的异常
    String errorMessage = e.getMessage();
    if (errorMessage != null && errorMessage.contains("Required request body is missing")) {
      log.error("缺少请求体异常 [TraceId: {}] - 请求路径: {}, 错误信息: {}",
          traceId, request.getRequestURI(), errorMessage, e);
      return CommonResult.result(ResponseEnum.PARAMS_ERROR.getCode(), "请求体不能为空");
    }

    // 检查是否是UnrecognizedPropertyException
    Throwable rootCause = e.getRootCause();
    if (rootCause instanceof UnrecognizedPropertyException) {
      return handleUnrecognizedPropertyException((UnrecognizedPropertyException) rootCause,
          request);
    }
    if (rootCause instanceof JsonParseException) {
      log.error("JSON解析异常 [TraceId: {}] - 请求路径: {}, 错误信息: {}",
          traceId, request.getRequestURI(), e.getMessage(), e);
      return CommonResult.result(ResponseEnum.INVALID_PARAM.getCode(),
          "请求格式错误，请检查JSON格式是否正确");
    }
    if(rootCause instanceof InvalidFormatException){
      InvalidFormatException invalidFormatException = (InvalidFormatException) rootCause;
      
      // 获取字段名
      String fieldName = "未知字段";
      if (invalidFormatException.getPath() != null && !invalidFormatException.getPath().isEmpty()) {
        fieldName = invalidFormatException.getPath().get(0).getFieldName();
      }
      
      // 获取目标类型
      String targetType = invalidFormatException.getTargetType().getSimpleName();
      
      // 获取输入值
      Object value = invalidFormatException.getValue();
      String valueStr = value != null ? value.toString() : "null";
      
      log.error("字段格式转换异常 [TraceId: {}] - 请求路径: {}, 字段名: {}, 输入值: {}, 目标类型: {}, 错误信息: {}",
          traceId, request.getRequestURI(), fieldName, valueStr, targetType, invalidFormatException.getMessage(), invalidFormatException);
      
      // 构造具体的错误信息
      String formatErrorMessage = String.format("字段 '%s' 的值 '%s' 格式不正确，无法转换为 %s 类型", 
          fieldName, valueStr, targetType);
      
      return CommonResult.result(ResponseEnum.INVALID_PARAM.getCode(), formatErrorMessage);
    }
    // 其他JSON解析异常
    log.error("HTTP消息不可读异常 [TraceId: {}] - 请求路径: {}, 错误信息: {}",
        traceId, request.getRequestURI(), e.getMessage(), e);
    return CommonResult.result(ResponseEnum.INVALID_PARAM.getCode(), "请求格式错误");
  }

  /**
   * 前端后端参数不匹配的异常，主要是前端多传递了字段
   */
  private CommonResult<Object> handleUnrecognizedPropertyException(UnrecognizedPropertyException e,
      HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();

    // 从异常中直接获取未知字段名（比正则解析更可靠）
    String unrecognizedField = e.getPropertyName();

    log.error("JSON反序列化异常 [TraceId: {}] - 请求路径: {}, 未识别字段: {}",
        traceId, request.getRequestURI(), unrecognizedField, e);

    // 构造清晰的错误信息
    String message = String.format("请求中包含未定义的参数：%s。请检查请求格式", unrecognizedField);
    return CommonResult.result(ResponseEnum.INVALID_PARAM.getCode(), "存在未定义的参数")
        .setData(message);
  }

  /**
   * 处理运行时异常
   */
  @ExceptionHandler(RuntimeException.class)
  public CommonResult<Object> handleRuntimeException(RuntimeException e,
      HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();
    log.error("运行时异常 [TraceId: {}] - 请求路径: {}, 错误信息: {}",
        traceId, request.getRequestURI(), e.getMessage(), e);
    return CommonResult.result(ResponseEnum.UNKNOWN_ERROR.getCode(), "系统内部错误");
  }

  /**
   * 处理其他未捕获的异常
   */
  @ExceptionHandler(Exception.class)
  public CommonResult<Object> handleException(Exception e, HttpServletRequest request) {
    String traceId = TraceContext.getTraceId();
    log.error("系统异常 [TraceId: {}] - 请求路径: {}, 错误信息: {}",
        traceId, request.getRequestURI(), e.getMessage(), e);
    return CommonResult.result(ResponseEnum.UNKNOWN_ERROR.getCode(), "系统异常，请联系管理员");
  }

  /**
   * 处理Feign调用异常
   * 将Feign异常转换为更友好的业务异常响应
   */
  @ExceptionHandler(FeignException.class)
  public CommonResult<Object> handleFeignException(FeignException e, HttpServletRequest request) {
    log.error("服务调用异常 - 请求路径: {}, 状态码: {}, 目标服务: {}",
        request.getRequestURI(), e.status(), e.request().url(), e);

    // 根据状态码处理不同类型的Feign异常
    if (e.status() == HttpStatus.NOT_FOUND.value()) {
      return CommonResult.result(ResponseEnum.SERVICE_UNAVAILABLE.getCode(), "请求的资源不存在");
    } else if (e.status() == HttpStatus.UNAUTHORIZED.value()) {
      return CommonResult.result(ResponseEnum.SERVICE_UNAVAILABLE.getCode(), "服务认证失败");
    } else if (e.status() == HttpStatus.BAD_REQUEST.value()) {
      return CommonResult.result(ResponseEnum.SERVICE_UNAVAILABLE.getCode(), "服务请求参数错误");
    } else {
      return CommonResult.result(ResponseEnum.SERVICE_UNAVAILABLE.getCode(),
          "服务调用失败，请稍后重试");
    }
  }

  /**
   * 获取TraceId，用于日志追踪
   * 子类可以重写此方法来自定义TraceId获取逻辑
   */
  protected String getTraceId() {
    return TraceContext.getTraceId();
  }

  /**
   * 构建错误响应的通用方法
   * 子类可以重写此方法来自定义响应格式
   */
  protected CommonResult<Object> buildErrorResponse(Integer code, String message, Object data) {
    return CommonResult.result(code, message).setData(data);
  }

  /**
   * 构建错误响应的简化方法
   */
  protected CommonResult<Object> buildErrorResponse(ResponseEnum responseEnum) {
    return CommonResult.result(responseEnum.getCode(), responseEnum.getMsg());
  }

  /**
   * 构建错误响应的简化方法（带数据）
   */
  protected CommonResult<Object> buildErrorResponse(ResponseEnum responseEnum, Object data) {
    return CommonResult.result(responseEnum.getCode(), responseEnum.getMsg()).setData(data);
  }
}